<template>
  <div class="page-container">
    <el-tabs v-model="activeName" @tab-click="handleClick">
      <el-tab-pane
        v-for="subsystem in subsystemList"
        :key="subsystem.id"
        :label="subsystem.name"
        :name="subsystem.id"
      />
    </el-tabs>
    <el-container>
      <el-aside width="200px" class="page-aside" v-drag v-collapse>
        <div class="title">
          <div class="title-content">菜单</div>
          <div class="title-button">
            <div
              v-if="(subsystemList.find(item => item.id === activeName) || {}).type !== '1'"
              class="button-list"
            >
              <div class="button-item">
                <ac-icon
                  iconId="018dca412c4e207388f7"
                  :style="'cursor: pointer;color: ' + (treeCheck ? 'rgb(0,133,246)' : '#999') + ';font-size: 18px;'"
                  :title="treeCheck ? '导入模式' : '导出模式'"
                  @click.stop="changeMode"
                />
              </div>
              <div class="button-item">
                <ac-icon
                  iconId="018ece4f749ec5911772"
                  style="cursor: pointer;color: rgb(0,133,246); font-size: 18px;"
                  title="导出菜单"
                  @click.stop="handleExport"
                />
              </div>
              <div class="button-item">
                <el-upload
                  action=""
                  :http-request="handleImport"
                  :show-file-list="false"
                  accept=".dat"
                >
                  <ac-icon
                    iconId="018ece4f70fcf0627f81"
                    style="cursor: pointer;color: rgb(0,133,246); font-size: 18px;"
                    title="导入菜单"
                  />
                </el-upload>
              </div>
            </div>
            <span class="title-button-box" @click="addMenu({})">
              <el-tooltip placement="bottom" content="新增菜单">
                <ac-icon iconId="018dc9c3efed46e8b927" size="18" />
              </el-tooltip>
            </span>
          </div>
        </div>
        <el-row>
          <el-input
            placeholder="菜单搜索"
            v-model="filterText"
            style="margin-right: 10px"
          ></el-input>
        </el-row>
        <el-row class="page-aside-grow">
          <el-tree 
            ref="tree"
            v-loading="menuLoading"
            element-loading-text="数据加载中..."
            class="tree"
            :data="treeData" 
            node-key="id"
            :props="treeProps" 
            :highlight-current="true"
            :default-expanded-keys="treeExpand"
            :expand-on-click-node="!treeCheck"
            @node-click="nodeClick" 
            :filter-node-method="filterNode"
            :show-checkbox="treeCheck"
          >
            <template v-slot="{ data }">
              <div class="node-item">
                <span class="node-label" :title="data.text"><span>{{data.text}}</span></span>
                <div class="node-button">
                  <el-dropdown trigger="hover" placement="bottom-start">
                    <span class="node-button-item">
                      <ac-icon iconId="018dca6283a1e534a046" @click.stop />
                    </span>
                    <template #dropdown>
                      <el-dropdown-menu>
                        <el-dropdown-item @click="addMenu(data.data)">
                          <ac-icon iconId="018dc9c3efb74ac607cb" color="#67C23A" style="margin-right: 5px" />新增菜单
                        </el-dropdown-item>
                        <el-dropdown-item @click="editMenu(data.data)">
                          <ac-icon iconId="018dca6db20c6a3aaf68" color="#0085f6" style="margin-right: 5px" />修改菜单
                        </el-dropdown-item>
                        <el-dropdown-item @click="moveMenu(data.data)">
                          <ac-icon iconId="018dca444e0754edef13" color="#FF6600" style="margin-right: 5px" />移动菜单
                        </el-dropdown-item>
                        <el-dropdown-item @click="deleteMenu(data.data)">
                          <ac-icon iconId="018dc9c3efd3a0502886" color="#F56C6C" style="margin-right: 5px" />删除菜单
                        </el-dropdown-item>
                      </el-dropdown-menu>
                    </template>
                  </el-dropdown>
                </div>
              </div>
            </template>
          </el-tree>
        </el-row>
      </el-aside>
      <el-main class="page-main">
        <!--表格内容栏-->
        <ac-table
          ref="dataTable"
          pkName="id"
          :filters="filters"
          :filterFields="filterFields"
          :columns="columns"
          :defaultSort="defaultSort"
          :checkRowOpShow="checkRowOpShow"
          @handleTableOperator="handleTableOperator"
          @handleAjaxSuccess="handleAjaxSuccess"
        ></ac-table>
      </el-main>
    </el-container>

    <!--新增编辑界面-->
    <el-dialog
      :title="operator"
      width="900px"
      v-model="dialogVisible"
      :close-on-click-modal="false"
      destroy-on-close
      :fullscreen="formData.type === '菜单'"
    >
      <div
        class="dialog-box"
        v-loading="dialogLoading"
        element-loading-text="数据加载中..."
      >
        <el-form
          ref="dataForm"
          label-width="120px"
          label-position="right"
          :model="formData"
          :rules="rules"
          :disabled="!dialogEdit"
          :hide-required-asterisk="!dialogEdit"
        >
          <el-row>
            <el-col :span="formData.type === '菜单' ? 7 : 11">
              <el-form-item label="菜单名称" prop="name">
                <el-input v-model="formData.name" />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11">
              <el-form-item label="菜单类型" prop="type">
                <ac-buttons v-model="formData.type" options='菜单,目录,页面' />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11">
              <el-form-item label="默认显示" prop="defaultShow">
                <ac-dict-switch v-model="formData.defaultShow" dictCode="dictYesNo" />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11">
              <el-form-item label="是否有效" prop="valid">
                <ac-dict-switch v-model="formData.valid" dictCode="dictYesNo" />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11">
              <el-form-item label="上级菜单" prop="parentId">
                <ac-tree
                  v-model="formData.parentId"
                  dataUrl="/system/menu/queryTree"
                  :query="{subsystemId: activeName}"
                  :leafSelect="false"
                />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11">
              <el-form-item label="排序" prop="ordinal">
                <ac-number v-model="formData.ordinal" :point="0" />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11" v-if="formData.type === '页面'">
              <el-form-item label="页面名称" prop="pageId">
                <ac-tree
                  v-model="formData.pageId"
                  dataUrl="/system/page/queryTree"
                  typeSelect="page"
                />
              </el-form-item>
            </el-col>
            <!--
            <el-col :span="formData.type === '菜单' ? 7 : 11" v-if="formData.type === '页面'">
              <el-form-item label="徽标脚本名称" prop="scriptId">
                <ac-tree
                  v-model="formData.scriptId"
                  dataUrl="/system/scriptGroovy/queryTree"
                  type-select="script"
                />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11" v-if="formData.type === '页面'">
              <el-form-item label="显示结果" prop="showResult">
                <ac-dict-switch v-model="formData.showResult" dictCode="dictYesNo" />
              </el-form-item>
            </el-col>
            -->
            <el-col :span="formData.type === '菜单' ? 7 : 11" v-if="formData.type === '菜单'">
              <el-form-item label="菜单路径" prop="vuePath">
                <el-input v-model="formData.vuePath" />
              </el-form-item>
            </el-col>
            <el-col :span="formData.type === '菜单' ? 7 : 11" v-if="formData.type === '菜单'">
              <el-form-item label="数据地址" prop="url">
                <el-input v-model="formData.url" />
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
        <ac-edit-table
          v-if="formData.type === '菜单'"
          ref="editTable"
          width="100%"
          tips="权限按钮"
          :columns="privilegeColumns"
          :rules="privilegeRules"
          :dataList="formData.privilegeList"
        ></ac-edit-table>
      </div>
      <template #footer>
        <el-button
          size="default"
          type="info"
          @click="dialogVisible=false"
        >取消</el-button>
        <el-button
          v-if="dialogEdit"
          size="default"
          type="primary"
          :loading="editLoading"
          @click="submitForm"
        >提交
        </el-button>
      </template>
    </el-dialog>

    <!--移动菜单-->
    <el-dialog
      v-model="moveVisible"
      title="移动菜单"
      width="500px"
      :close-on-click-modal="false"
      destroy-on-close
      draggable
    >
      <el-form
        ref="moveForm"
        label-width="120px"
        label-position="right"
        :model="moveData"
      >
        <el-row>
          <el-col :span="24">
            <el-form-item label="菜单">
              <ac-input
                v-model="moveData.name"
                :disabled="true"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="移动到" prop="parentId">
              <ac-tree
                v-model="moveData.parentId"
                dataUrl="/system/menu/queryTree"
                :query="{subsystemId: activeName}"
                :leafSelect="false"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button
          size="default"
          type="info"
          @click="moveVisible=false"
        >取消</el-button>
        <el-button
          type="primary"
          size="default"
          :loading="moveLoading"
          @click="submitMoveForm"
        >提交</el-button>
      </template>
    </el-dialog>

    <el-dialog
      v-model="relationVisible"
      :title="relationTitle"
      :close-on-click-modal="false"
      destroy-on-close
      fullscreen
    >
      <relation-page :pageId="relationPageId" />
    </el-dialog>
  </div>
</template>

<script setup>
import { onMounted, ref, watch, nextTick } from 'vue';
import { ElMessage, ElMessageBox } from "element-plus";
import api from '@/http/api';
import RelationPage from "@/admin/codeless/relationPage.vue";

const defaultFormData = ref({
  id: '',
  parentId: '0',
  type: '页面',
  name: '',
  icon: '',
  vuePath: '',
  url: '',
  defaultShow: '0',
  valid: '1',
  needPermit: '1',
  ordinal: 1,
  privilegeList: []
});

const activeName = ref('');
const subsystemList = ref([]);

const filterText = ref('');
const treeData = ref([]);
const treeProps = ref({
  children: 'children',
  label: 'text'
});
const treeExpand = ref(['0']);
const menuLoading = ref(true);

const parentId = ref('');
const currentId = ref('');

const filters = ref({
  name: '',
  type: '',
  valid: '',
  subsystemId: '',
});
const filterFields = ref([
  {prop: "name", label: "菜单名称"},
]);
const columns = ref([
  {prop: "name", label: "名称", minWidth: 160},
  {prop: "id", label: "编码", width: 170},
  {prop: "type", label: "类型", width: 70, align: 'center', type: 'radio', options: '菜单,目录,页面'},
  {prop: "vuePath", label: "菜单路径", minWidth: 160, table: () => activeName.value === '0'},
  {prop: "url", label: "数据地址", minWidth: 160, table: () => activeName.value === '0'},
  {prop: "pageName", label: "页面名称", minWidth: 160, table: () => activeName.value !== '0'},
  {prop: "defaultShow", label: "默认显示", width: 100, align: 'center', type: 'dict-radio', dictCode: "dictYesNo"},
  {prop: "valid", label: "是否有效", width: 100, align: 'center', type: 'dict-radio', dictCode: "dictYesNo"},
  {prop: "ordinal", label: "排序", width: 70, type: 'number' },
  {prop: "updateTime", label: "更新时间", width: 160, onlyShow: true, type: "datetime" },
  {prop: "createTime", label: "创建时间", width: 160, onlyShow: true, table: false, type: "datetime" }
]);
const operator = ref('');
const dialogVisible = ref(false);
const dialogEdit = ref(false);
const dialogLoading = ref(false);
const editLoading = ref(false);
const formData = ref(defaultFormData.value);
const rules = ref({
  name: [{required: true, message: '请输入菜单名称', trigger: 'blur'}],
  type: [{required: true, message: '请选择菜单类型', trigger: ['blur', 'change']}],
  valid: [{required: true, message: '请选择是否有效', trigger: ['blur', 'change']}],
  ordinal: [
    {required: true, message: '请输入排序', trigger: 'blur'},
    {type: 'number', message: '排序必须为数字', trigger: 'blur'}]
});
const privilegeColumns = ref([
  {prop: "name", label: "名称", minWidth: 110},
  {prop: "type", label: "类型", width: 80, type: 'select', options: '按钮,权限'},
  {prop: "buttonLevel", label: "按钮级别", width: 80, type: 'select', options: '表级,行级,全部', show: (data) => data['type'] === '按钮'},
  {prop: "buttonClass", label: "按钮样式", width: 80, type: 'select', allowCreate: true, options: () => {
      return [
        {key: 'primary', value: '蓝色'},
        {key: 'success', value: '绿色'},
        {key: 'warning', value: '黄色'},
        {key: 'info', value: '灰色'},
        {key: 'danger', value: '红色'},
      ]
    },
    show: (data) => data['type'] === '按钮'
  },
  {prop: "buttonIcon", label: "按钮图标", width: 130, type: 'icon-select',
    allowCreate: true, show: (data) => data['type'] === '按钮'
  },
  {prop: "buttonType", label: "按钮类型", width: 130, type: 'select', options: () => {
      return [
        {key: 'add', value: '新增'},
        {key: 'addSub', value: '新增子项'},
        {key: 'edit', value: '编辑'},
        {key: 'delete', value: '删除'},
        {key: 'ajax', value: 'Ajax提交'},
        {key: 'downloadExcel', value: 'Excel下载'},
        {key: 'importExcel', value: 'Excel导入'},
        {key: 'open', value: '开启'},
        {key: 'close', value: '关闭'},
      ]
    },
    allowCreate: true, show: (data) => data['type'] === '按钮'
  },
  {prop: "selectedMode", label: "选择方式", width: 110, type: 'select', options: () => {
      return [
        {key: '1', value: '不用选择'},
        {key: '2', value: '单选(必选)'},
        {key: '3', value: '多选(可选)'},
        {key: '4', value: '多选(必选)'},
      ]
    },
    show: (data) => data['type'] === '按钮'
  },
  {prop: "dataSourUrl", label: "数据来源地址", minWidth: 140, show: (data) => data['type'] === '按钮'},
  {prop: "dataDestUrl", label: "数据目标地址", minWidth: 140, show: (data) => data['type'] === '按钮'},
  {prop: "clickTips", label: "点击提示", minWidth: 120, show: (data) => data['type'] === '按钮'},
  {prop: "ordinal", label: "排序", width: 60, type: 'number'},
]);
const privilegeRules = ref({
  operatorName: [{required: true, message: '请输入操作名称', trigger: 'blur'}],
  buttonName: [{required: true, message: '请输入按钮名称', trigger: 'blur'}],
  buttonLevel: [{required: true, message: '请选择按钮级别', trigger: 'change'}],
  buttonType: [{required: true, message: '请选择按钮类型', trigger: 'change'}],
  selectedMode: [{required: true, message: '请选择选择方式', trigger: 'change'}],
  ordinal: [
    {required: true, message: '请输入排序', trigger: 'blur'},
    {type: 'number', message: '排序必须为数字', trigger: 'blur'}]
});

const defaultSort = ref({prop: 'ordinal', order: 'ascending'});
const tree = ref();
const dataTable = ref();
const dataForm = ref();
const editTable = ref();

const moveVisible = ref(false);
const moveLoading = ref(false);
const moveForm = ref();
const moveData = ref({});

const relationVisible = ref(false);
const relationTitle = ref('');
const relationPageId = ref('');
const treeCheck = ref(false);

const changeMode = () => {
  treeCheck.value = !treeCheck.value;
  if (treeCheck.value) {
    tree.value.setCheckedNodes([]);
  }
}

const handleExport = () => {
  const nodes = tree.value.getCheckedNodes(false, true);
  if (!nodes || nodes.length === 0) {
    ElMessage.error("请先选择菜单！");
    return;
  }
  const data = nodes.map(item => item.id);
  api.downloadFile('/system/menu/exportData', {ids: data}).then(res => api.saveFileLocal(res));
};

const handleImport = (file) => {
  api.uploadFile('/system/menu/importData', {'file': file.file}).then((res) => {
    ElMessage.success(res.message);
    loadTreeData(activeName.value);
  });
};

const handleClick = (tab) => {
  treeCheck.value = false;
  filters.value.parentId = '';
  activeName.value = tab.paneName;
  loadTreeData(activeName.value);
};

const filterNode = (value, data) => {
  if (!value) return true;
  return data.text.indexOf(value) >= 0;
};

const currNode = ref(null);
const nodeClick = (data, treeNode) => {
  if (currNode.value && currNode.value.id === data.id) {
    treeNode.checked = !treeNode.checked;
  } else {
    treeNode.checked = true;
  }

  if (treeNode.checked) {
    currNode.value = data;
    filters.value.parentId = data.id;
    parentId.value = data.pid || '0';
    currentId.value = data.id || '0';
    dataTable.value.findPage();
  } else {
    tree.value.setCurrentKey(null);
    currNode.value = null;
    parentId.value = '0';
    currentId.value = '0';
    filters.value.parentId = '';
    dataTable.value.refreshPage(1);
  }
};

const checkRowOpShow = (op, row) => {
  return op.buttonType !== 'fast' || (op.buttonType === 'fast' && row.type === '页面' && row.pageId);
}

const handleTableOperator = (button, row) => {
  operator.value = button.name
  formData.value = Object.assign({}, defaultFormData.value);
  dialogEdit.value = true;

  if (button.buttonType === 'edit') {
    dialogLoading.value = true;
    api.post('/system/menu/queryBy', {id: row.id}).then(res => {
      dialogLoading.value = false;
      formData.value = Object.assign({}, res.data, {button: button});
    }).catch(() => {
      dialogLoading.value = false;
    });
  } else if (button.buttonType === 'copy') {
    dialogLoading.value = true;
    api.post('/system/menu/queryBy', {id: row.id}).then(res => {
      dialogLoading.value = false;
      const result = res.data;
      result.id = '';
      (result.privilegeList || []).forEach(item => item.id = '');
      formData.value = Object.assign({}, result, {button: button});
    }).catch(() => {
      dialogLoading.value = false;
    });
  } else if (button.buttonType === 'add') {
    formData.value = Object.assign({},
      defaultFormData.value,
      {button: button, parentId: filters.value.parentId || '0'});
  } else if (button.buttonType === 'addSub') {
    if (currentId.value === '') {
      ElMessage.warning('请先选择右左侧菜单！');
      return;
    }
    formData.value = Object.assign({},
      defaultFormData.value,
      {button: button, parentId: filters.value.parentId || '0'});
  } else if(button.buttonType === 'fast') {
    relationTitle.value = button.name + '【' + row.name + '】';
    relationPageId.value = row.pageId;
    relationVisible.value = true;
    return;
  } else {
    formData.value = Object.assign({button: button}, defaultFormData.value);
  }

  dialogVisible.value = true
};

const handleAjaxSuccess = () => {
  loadTreeData(activeName.value);
};

const addMenu = (data) => {
  operator.value = '新增菜单';
  dialogLoading.value = false;
  dialogEdit.value = true;
  formData.value = Object.assign({},
    {parentId: data.id || '', type: activeName.value === '0' ? '菜单' : '页面', valid: '1', ordinal: 1},
    {button: {dataDestUrl: '/system/menu/save'}});
  dialogVisible.value = true;
};

const editMenu = (data) => {
  operator.value = '编辑菜单';
  dialogLoading.value = true;
  dialogEdit.value = true;
  api.post('/system/menu/queryBy', {id: data.id}).then(res => {
    dialogLoading.value = false;
    formData.value = Object.assign({}, res.data, {button: {dataDestUrl: '/system/menu/save'}});
    dialogVisible.value = true;
  }).catch(() => {
    dialogLoading.value = false;
  });
};

const moveMenu = (data) => {
  moveVisible.value = true;
  moveData.value = { id: data.id, name: data.name, parentId: data.parentId };
  moveForm.value && moveForm.value.resetFields();
};

const submitMoveForm = () => {
  moveForm.value && moveForm.value.validate((valid) => {
    if (valid) {
      ElMessageBox.confirm('请确认是否提交？', '提示', {type: 'info'}).then(() => {
        moveLoading.value = true;
        const data = {id: moveData.value.id, parentId: moveData.value.parentId};
        const url = '/system/menu/updateParentId';
        api.post(url, data).then(res => {
          ElMessage.success(res.message);
          moveVisible.value = false;
          moveLoading.value = false;
          treeExpand.value.push(res.data.id || '0');
          loadTreeData(activeName.value);
        }).catch(() => {
          moveLoading.value = false;
        });
      }).catch(() => {
      });
    }
  });
};

const deleteMenu = (data) => {
  const url = '/system/menu/delete';
  ElMessageBox.confirm('请确认是否删除【' + data.name + '】？', '提示', {type: 'warning'}).then(() => {
    api.post(url, {id: data.id}).then(res => {
      ElMessage.success(res.message);
      treeExpand.value.push(data.parentId || '0');
      loadTreeData(activeName.value);
    });
  }).catch(() => {
  });
};

const submitForm = () => {
  dataForm.value && dataForm.value.validate((valid) => {
    if (valid) {
      if (editTable.value) {
        editTable.value.validate(valid => {
          if (valid) {
            doSubmit();
          }
        });
      } else {
        doSubmit();
      }
    }
  });
};

const doSubmit = () => {
  ElMessageBox.confirm('请确认是否提交？', '提示', {type: 'info'}).then(() => {
    editLoading.value = true;
    formData.value.subsystemId = activeName.value;
    formData.value.privilegeList = editTable.value && editTable.value.getList() || [];
    api.post(formData.value.button.dataDestUrl, formData.value).then(res => {
      ElMessage.success(res.message);
      dialogVisible.value = false;
      editLoading.value = false;
      dataTable.value.findPage();
      treeExpand.value.push(res.data.id || '0');
      loadTreeData(activeName.value);
    }).catch(() => {
      editLoading.value = false;
    });
  }).catch(()=>{});
};

const loadTreeData = (subsystemId) => {
  menuLoading.value = true;
  api.post('/system/menu/queryTree', {subsystemId: subsystemId}).then(res => {
    treeData.value = res.data.children || [];
    menuLoading.value = false;
  }).catch(() => {
    menuLoading.value = false;
  });

  filters.value.subsystemId = subsystemId;
  nextTick(() => {
    dataTable.value && dataTable.value.refreshPage();
  })
};

const loadSubsystem = () => {
  api.post('/business/subsystem/list').then(res => {
    if (res.code === '1') {
      subsystemList.value = res.data || [];
      if (subsystemList.value.length > 0) {
        activeName.value = subsystemList.value[0].id;
        loadTreeData(activeName.value);
      }
    }
  });
};

watch(() => filterText.value, (val) => {
  tree.value.filter(val)
});

onMounted(() => {
  loadSubsystem();
});
</script>

<style lang="scss" scoped>
.dialog-box {
  display: flex;
  flex-direction: column;
}

.title {
  display: flex;
  flex-direction: row;
  margin-right: 10px;
  height: 40px;
  align-items: center;

  .title-content {
    flex-grow: 1;
    font-size: 18px;
  }
  .title-button {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
  }
  .title-button .button-list {
    display: flex;
    flex-direction: row;
    align-items: center;
    .button-item {
      margin: 0 5px 0 5px;
    }
  }
  .title-button .title-button-box {
    height: 30px;
    width: 40px;
    border-radius: 5px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    border: none;
    cursor: pointer;
  }

  .title-button .title-button-box:hover {
    background: #eaeaea;
    border: none;
  }

  .title-button .title-button-box:focus-visible {
    outline: unset;
  }
}

.filter-box {
  margin-right: 10px;
}

.tree :deep(.el-tree-node__content) {
  height: 40px!important;
}

.page-aside-grow {
  width: 100% !important;

  :deep(.el-tree) {
    display: flex;
    flex-direction: column;
    width: 1px !important;
  }

  :deep(.el-tree-node) {
    display: flex;
    flex-direction: column;
    width: 100% !important;
  }

  :deep(.el-tree-node__content) {
    display: flex;
    flex-grow: 1;
  }
}

.node-item {
  display: flex;
  flex-direction: row;
  align-items: center;
  width: 100%;

  .node-label {
    flex-grow: 1;
    width: 1px;
    text-overflow: ellipsis;
    overflow: hidden;
    display: flex;

    > span {
      width: 100%;
      text-overflow: ellipsis;
      overflow: hidden;
    }
  }
  .node-button {
    margin-right: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .node-button .node-button-item {
    height: 30px;
    width: 40px;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 5px;
  }
  .node-button .node-button-item:focus-visible {
    outline: unset;
  }
}
.node-item:hover {
  .node-button .node-button-item:hover {
    background: #eaeaea;
  }
}
</style>